#include "base/Surface.h"
#include "Constants.h"
#include <vector>


Surface::Surface(GLuint programHandler, GLShaderConfig shaderConfig, float widthX, float widthY) :
		GLObject(programHandler, shaderConfig),
		widthX(widthX),
		widthY(widthY) {
	GLShaderConfig* config = GLObject::getShaderConfig();

	createSurface(1.0f, widthX, widthY, 50);

	/**
	 * POSITION DATA
	 */
	BufferConfig positionBuffer;
	positionBuffer.target = GL_ARRAY_BUFFER;
	positionBuffer.size = positionSize * sizeof(float);
	positionBuffer.data = positionData;
	positionBuffer.usage = GL_STATIC_DRAW;

	VertexAttrib positionAttrib;
	positionAttrib.index = config->getVertexAttribIndex(GLShaderConfig::VERTEX_POSITION_TAG);
	positionAttrib.size = 3;
	positionAttrib.type = GL_FLOAT;
	positionAttrib.normalized = GL_FALSE;
	positionAttrib.stride = 0;
	positionAttrib.pointer = NULL;

	config->addVertexAttribInfo(GLShaderConfig::VERTEX_POSITION_TAG, positionAttrib, positionBuffer);

	/**
	 * NORMAL DATA
	 */
	BufferConfig normalBuffer;
	normalBuffer.target = GL_ARRAY_BUFFER;
	normalBuffer.size = positionSize * sizeof(float);
	normalBuffer.data = normalData;
	normalBuffer.usage = GL_STATIC_DRAW;

	VertexAttrib normalAttrib;
	normalAttrib.index = config->getVertexAttribIndex(GLShaderConfig::VERTEX_NORMAL_TAG);
	normalAttrib.size = 3;
	normalAttrib.type = GL_FLOAT;
	normalAttrib.normalized = GL_FALSE;
	normalAttrib.stride = 0;
	normalAttrib.pointer = NULL;

	config->addVertexAttribInfo(GLShaderConfig::VERTEX_NORMAL_TAG, normalAttrib, normalBuffer);

	/**
	 * TEXTURE DATA
	 */
	/*BufferConfig textureBuffer;
	textureBuffer.target = GL_ARRAY_BUFFER;
	textureBuffer.size = 2 * 24 * sizeof(float);
	textureBuffer.data = textureData;
	textureBuffer.usage = GL_STATIC_DRAW;

	VertexAttrib textureAttrib;
	textureAttrib.index = config->getVertexAttribIndex(GLShaderConfig::VERTEX_TEXTURE_COORD_TAG);
	textureAttrib.size = 2;
	textureAttrib.type = GL_FLOAT;
	textureAttrib.normalized = GL_FALSE;
	textureAttrib.stride = 0;
	textureAttrib.pointer = NULL;

	config->addVertexAttribInfo(GLShaderConfig::VERTEX_TEXTURE_COORD_TAG, textureAttrib, textureBuffer);*/
}

Surface::~Surface() {
	if (positionData != NULL) {
		delete[] positionData;
	}

	if (normalData != NULL) {
		delete[] normalData;
	}

	if (indexData != NULL) {
		delete[] indexData;
	}
}

void Surface::createSurface(const float radius, float width, float height, const unsigned int segmentsPerLoop)
{
	std::vector<float> vertex_buffer;
	std::vector<float> normal_buffer;
	std::vector<unsigned int> index_buffer;

	float vertexDistanceX = width / segmentsPerLoop;
	float vertexDistanceY = height / segmentsPerLoop;

	for (unsigned int segmentY = 0; segmentY < segmentsPerLoop; segmentY++) {
		float posY = segmentY * vertexDistanceY;
		for (unsigned int segmentX = 0; segmentX < segmentsPerLoop; segmentX++) {
			float posX = segmentX * vertexDistanceX;
			vertex_buffer.push_back(posX);
			vertex_buffer.push_back(posY);
			vertex_buffer.push_back(0);

			normal_buffer.push_back(0);
			normal_buffer.push_back(0);
			normal_buffer.push_back(1);
		}
	}

	for (unsigned int segmentY = 0; segmentY < segmentsPerLoop - 1; segmentY++) {
		for (unsigned int segmentX = 0; segmentX < segmentsPerLoop - 1; segmentX++) {
			index_buffer.push_back(segmentX + (segmentY*segmentsPerLoop));
			index_buffer.push_back(segmentX + (segmentY*segmentsPerLoop) + segmentsPerLoop);
			index_buffer.push_back((segmentX+1) + (segmentY*segmentsPerLoop));

			index_buffer.push_back(segmentX + (segmentY*segmentsPerLoop) + segmentsPerLoop);
			index_buffer.push_back((segmentX+1) + (segmentY*segmentsPerLoop));
			index_buffer.push_back((segmentX+1) + (segmentY*segmentsPerLoop) + segmentsPerLoop);
		}
	}

	if (positionData != NULL)
	{
		delete[] positionData;
	}
	positionData = new float[vertex_buffer.size()];

	if (normalData != NULL)
	{
		delete[] normalData;
	}
	normalData = new float[normal_buffer.size()];

	if ( indexData != NULL)
	{
		delete[] indexData;
	}
	indexData = new unsigned int[index_buffer.size()];

	std::vector<float>::iterator vertex_it;
	unsigned int v = 0;
	for (vertex_it = vertex_buffer.begin(); vertex_it != vertex_buffer.end(); vertex_it++)
	{
		positionData[v] = *vertex_it;
		v++;
	}

	std::vector<float>::iterator normal_it;
	unsigned int n = 0;
	for (normal_it = normal_buffer.begin(); normal_it != normal_buffer.end(); normal_it++)
	{
		normalData[n] = *normal_it;
		n++;
	}

	std::vector<unsigned int>::iterator index_it;
	unsigned int i = 0;
	for (index_it = index_buffer.begin(); index_it != index_buffer.end(); index_it++)
	{
		indexData[i] = *index_it;
		i++;
	}

	indexSize = index_buffer.size();
	positionSize = vertex_buffer.size();

}

void Surface::render(glm::mat4 viewMatrix, glm::mat4 modelMatrix) {

	changeObjectColor(colorR, colorG, colorB);
	// Normal Matrix
	glm::mat3 normal_matrix = glm::mat3 ( 1.0f );
	// normal_matrix =
	glm::mat4 aux = viewMatrix * modelMatrix * transformMatrix;
	for (int i=0; i<3; i++)
		for (int j=0; j<3; j++)
			normal_matrix[i][j] = aux[i][j];


	// Bind Object Transformation Matrix
	GLuint locationTransformMatrix = glGetUniformLocation( this->programHandler, "TransformMatrix");
	if( locationTransformMatrix >= 0 )
	{
		glUniformMatrix4fv( locationTransformMatrix, 1, GL_FALSE, &transformMatrix[0][0]);
	}

	// Bind Model Matrix
	GLuint locationModelMatrix = glGetUniformLocation( this->programHandler, "ModelMatrix");
	if( locationModelMatrix >= 0 )
	{
		glUniformMatrix4fv( locationModelMatrix, 1, GL_FALSE, &modelMatrix[0][0]);
	}

	// Bind View MAtrix
	GLuint locationViewMatrix = glGetUniformLocation( this->programHandler, "ViewMatrix");
	if( locationViewMatrix >= 0 )
	{
		glUniformMatrix4fv( locationViewMatrix, 1, GL_FALSE, &viewMatrix[0][0]);
	}

	// Bind Normal MAtrix
	GLuint locationNormalMatrix = glGetUniformLocation( this->programHandler, "NormalMatrix");
	if( locationNormalMatrix >= 0 )
	{
		glUniformMatrix3fv( locationNormalMatrix, 1, GL_FALSE, &normal_matrix[0][0]);
	}

	glVertexPointer(3, GL_FLOAT, 0, positionData);
	glEnableClientState(GL_VERTEX_ARRAY);
	glDrawElements( GL_TRIANGLES, indexSize, GL_UNSIGNED_INT, indexData);
}

void Surface::update(glm::mat4 viewMatrix, glm::mat4 modelMatrix) {
	init();
	render(viewMatrix, modelMatrix);
}
